
package syscall

import (
	"errors"
	"unsafe"
)

//extern as_area_create
func as_area_create(base unsafe.Pointer, size _size_t, flags uint) unsafe.Pointer

//extern as_area_resize
func as_area_resize(address unsafe.Pointer, size _size_t, flags uint) int

//extern as_area_destroy
func as_area_destroy(address unsafe.Pointer) int

//extern as_area_change_flags
func as_area_change_flags(address unsafe.Pointer, flags uint) int

//extern as_get_physical_mapping
func as_get_physical_mapping(virt unsafe.Pointer, phys *uintptr) int

//extern memset
func memset(dest unsafe.Pointer, value int, size _size_t) unsafe.Pointer

type MemArea struct {
	base unsafe.Pointer
	size int
	flags uint
}

func UnsafeCreateArea(base unsafe.Pointer, size int, flags uint) *MemArea {
	if (size % PAGE_SIZE) != 0 {
		panic("Invalid size to syscall.UnsafeCreateArea(): not multiple of page size.")
	}
	
	return &MemArea{base, size, flags}
}

func CreateArea(base uintptr, size int, flags uint) (*MemArea, error) {
	if size % PAGE_SIZE != 0 {
		// Round up to a multiple of page size.
		size = ((size - 1) / PAGE_SIZE + 1) * PAGE_SIZE
	}
	if size != int(_size_t(size)) {
		// Check for overflow.
		return nil, ERANGE
	}

	realbase := as_area_create(unsafe.Pointer(base), _size_t(size), flags)
	if uintptr(realbase) == AS_MAP_FAILED {
		return nil, errors.New("syscall.CreateArea() failed")
	}
	
	if flags & AS_AREA_WRITE != 0 {
		// Zero out the created area.
		memset(realbase, 0, _size_t(size))
	}
	
	return &MemArea{realbase, size, flags}, nil
}

func (area *MemArea) Size() int {
	return area.size
}

func (area *MemArea) Resize(size int, flags uint) error {
	if size % PAGE_SIZE != 0 {
		// Round up to a multiple of page size.
		size = ((size - 1) / PAGE_SIZE + 1) * PAGE_SIZE
	}
	if size != int(_size_t(size)) {
		// Check for overflow.
		return ERANGE
	}

	rc := as_area_resize(area.base, _size_t(size), flags)
	if rc != 0 {
		return Errno(rc)
	}
	
	area.size = size
	return nil
}

func (area *MemArea) Flags() uint {
	return area.flags
}

func (area *MemArea) ChangeFlags(flags uint) error {
	rc := as_area_change_flags(area.base, flags)
	if rc != 0 {
		return Errno(rc)
	}
	area.flags = flags
	return nil
}

func (area *MemArea) PhysicalMapping() (phys uintptr) {
	as_get_physical_mapping(area.base, &phys)
	return
}

func (area *MemArea) Destroy() error {
	rc := as_area_destroy(area.base)
	if rc != 0 {
		return Errno(rc)
	}
	
	area.base = nil
	area.size = 0
	area.flags = 0
	return nil
}

//extern unsafe_set_slice
func unsafeSetSlice(slice *[]byte, data *byte, size int)

func (area *MemArea) Slice() (result []byte) {
	unsafeSetSlice(&result, (*byte)(area.base), area.size)
	return
}

